class Person:
    pass

# 基于Person类可以创建N多个实例，称为多例模式
p1 = Person()
p2 = Person()

print(p1 is p2)  # False

print(id(p1))
print(id(p2))


"""
单例模式的意思是，无论我创建了多少次实例，这些实例都是同一个东西。
其中，“单例模式”（Singleton Pattern）和“多例模式”（Multiton Pattern）
都是用来控制类实例的数量，但它们的目的和使用场景略有不同。

单例模式是一种常用的软件设计模式，它的目的是确保某个类只有一个实例，并提供一个全局访问点。
这种模式经常被用在只需要一个实例的地方，
比如线程池、缓存、对话框、注册表设置、设备驱动程序对象等。
"""

"""
实现的方式有两种：
1、基于__new__函数实现单例
new方法负责创建一个实例对象，在对象被创建的时候调用该方法它是一个类方法。
new方法在返回一个实例之后，会自动的调用init方法，对实例进行初始化。
如果new方法不返回值，或者返回的不是实例，那么它就不会自动取调用init方法。

init方法负责将该实例对象进行初始化，在对象被创建之后调用该方法，在new方法创建出一个实例后
对实例属性进行初始化。init方法可以没有返回值。

call方法其实和类的创建过程和实例化没有多大关系，定义了call方法才能被使用函数的方式执行。

打个比方帮助理解：如果将创建实例的过程比作建房子。
- 那么class就是一个房屋的设计图，他规定了这个房子有几个房间，每个人房间的大小朝向等。
    这个设计图就是类的结构
- new就是这个房屋的框架，每个具体的房屋都需要先搭好框架才能进行装修，当然先有了房屋设计
    才能有具体的房屋框架出来，这个就是从类到类实例的创建。
- init就是装修房子的过程，对房屋的墙面和地板等颜色材质的丰富就是它该做的事情。当然也是先有
    具体的房子框架才能进行装饰。这个就是实例属性的初始化，他是在new出一个实例后才能初始化
- call就是房子的电话，有了固定电话，才能被打电话（就是通过括号的方式像函数一样执行）


"""

class SingletonPerson(object):

    def __new__(cls, *args, **kwargs):
        # 采用当前类的私有属性来存放实例对象
        if not hasattr(cls, '_instance'):
            cls._instance = super(SingletonPerson, cls).__new__(cls, *args, **kwargs)

        return cls._instance


p1 = SingletonPerson()

p2 = SingletonPerson()

print(p1 is p2)  # True



"""
实现的方式有两种：
2、基于元类来实现单例

只使用元类的知识，定义一个单例模式
使用我们定义的元类创建出来的类，都是单例的
"""
class SingletonMetaClass(type):

    def __init__(self, *args, **kwargs):
        print('开始执行元类中的__init__函数')
        self._instance = None
        super(SingletonMetaClass, self).__init__(*args, **kwargs)


    def __call__(self, *args, **kwargs):
        print('开始执行元类中的__call__函数')
        if self._instance is None:
            self._instance = super(SingletonMetaClass, self).__call__(*args, **kwargs)
        return self._instance


class Person(object, metaclass=SingletonMetaClass):
    pass


p3 = Person()
p4 = Person()

print(p3 is p4)  # True
